package a3_p04_ng_sw;


import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JTextField;

import org.jgrapht.VertexFactory;
import org.jgrapht.graph.AbstractGraph;
import org.jgrapht.graph.DefaultDirectedWeightedGraph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.DefaultWeightedEdge;
import org.jgrapht.graph.DirectedPseudograph;
import org.jgrapht.graph.Pseudograph;
import org.jgrapht.graph.WeightedPseudograph;
import org.jgrapht.generate.CompleteGraphGenerator;

public class CreateDia extends JDialog implements SearchTyps{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private static CreateDia instance;
	private static int dialogTyp;
	private int anzahlKanten;
	private Main mainFrame;
	
	private CreateDia(int typ, Main frame){
		dialogTyp = typ;
		switch(dialogTyp){
		case random:
			initRandom();
			mainFrame = frame;
			break;
		case userCase:
			
			break;
		}
	}
	
	
	public static CreateDia getInstance(int typ, Main frame){
		if(instance == null || typ != dialogTyp) instance = new CreateDia(typ, frame);
		instance.setVisible(true);
		return instance;
	}
	
	
	private void initRandom(){	
		this.setResizable(false);
		this.setLayout(null);
		checkGewichtet.setBounds(new Rectangle(5,5,100,21));
		checkAttr.setBounds(new Rectangle(110,5, 100,21));
		checkGerichtet.setBounds(new Rectangle(220,5,100,21));
		
		lblAnzahlKnoten.setBounds(new Rectangle(5,45,120,21));
		txtAnzahlKnoten.setBounds(new Rectangle(120,45,60,21));
		
		btnCreate.setBounds(new Rectangle(75,90,150,21));
		btnCancel.setBounds(new Rectangle(235,90,80,21));
		
		this.getContentPane().add(btnCancel,null);
		this.getContentPane().add(btnCreate,null);
		
		this.getContentPane().add(checkGewichtet,null);
		this.getContentPane().add(checkAttr,null);
		this.getContentPane().add(checkGerichtet,null);
		
		this.getContentPane().add(lblAnzahlKnoten,null);
		this.getContentPane().add(txtAnzahlKnoten,null);
		
		this.setSize(380,150);
		
		actions();
	}
	
	private void actions(){
		btnCreate.addActionListener(new ActionListener() {			
			@Override
			public void actionPerformed(ActionEvent e) {
				switchGraph();
				switch(dialogTyp){
				case random:
					randomAction(e);
					break;
				case userCase:
					
					break;					
				}
			}
		});
		btnCancel.addActionListener(new ActionListener() {		
			@Override
			public void actionPerformed(ActionEvent e) {
				cancelAction(e);	
			}
		});
	}
	
	void randomAction(ActionEvent e){
		AbstractGraph<? , ?> aGraph;
		
		
		try{
			anzahlKanten = Integer.valueOf(txtAnzahlKnoten.getText()).intValue();
			if(anzahlKanten <0){
				JOptionPane.showMessageDialog(this, "Hier dürfen nur Elemente der\n" +
						"Natürlichenzahlen eingegeben werden!\n" +
						"Und "+ anzahlKanten +" gehört nicht dazu", "Fehler...", JOptionPane.ERROR_MESSAGE);
				return;
			}				
			if(isGewichtet){
				aGraph = createGewichtetGraph();
				
			}else{
				aGraph = createUngewichtetGraph();
			}
			
			mainFrame.addGraph(aGraph, anzahlKanten, "");			
		}catch (NumberFormatException ne) {
			JOptionPane.showMessageDialog(this, "Hier dürfen nur Elemente der\n" +
					"Natürlichenzahlen eingegeben werden!\n" +
					"Und "+ txtAnzahlKnoten.getText() +" gehört nicht dazu", "Fehler...", JOptionPane.ERROR_MESSAGE);
		}
		
	}
	
	///////////////////////////////////////////////////
	// Oberste ebene
	/////////////////
	
	private AbstractGraph<?, DefaultWeightedEdge> createGewichtetGraph(){
		if(isAttr){
			return createGewichtetAttrGraph();
		}else{
			return createGewichtetUnAttrGraph();
		}		
	}
	
	private AbstractGraph<?, DefaultEdge> createUngewichtetGraph(){
		if(isAttr){
			return createAttrUnGewichtetGraph();
		}else{
			return createUnAttrUnGewichtetGraph();
		}
	}
	
	///////////////////////////////////
	// Mittel Ebenen
	///////////////////////////////////////

	
	private AbstractGraph<String, DefaultWeightedEdge> createGewichtetUnAttrGraph() {
		AbstractGraph<String, DefaultWeightedEdge> tGraph;
		if(isGerichtet){
			tGraph = new DefaultDirectedWeightedGraph<String, DefaultWeightedEdge>(DefaultWeightedEdge.class);
			return buildGerichtetUnAttrGewichtetGraph(tGraph);
		}else{
			tGraph = new WeightedPseudograph<String, DefaultWeightedEdge>(DefaultWeightedEdge.class);
			return buildUnGerichtetUnAttrGewichtetGraph(tGraph);
		}
	}

	private AbstractGraph<Knoten, DefaultWeightedEdge> createGewichtetAttrGraph(){
		AbstractGraph<Knoten, DefaultWeightedEdge> tGraph;
		if(isGerichtet){
			tGraph = new DefaultDirectedWeightedGraph<Knoten, DefaultWeightedEdge>(DefaultWeightedEdge.class);
			return buildGerichtetAttrGewichtetGraph(tGraph);
		}else{
			tGraph = new WeightedPseudograph<Knoten, DefaultWeightedEdge>(DefaultWeightedEdge.class);
			return buildUnGerichtetAttrGewichtetGraph(tGraph);
		}		
	}	

	private AbstractGraph<Knoten, DefaultEdge> createAttrUnGewichtetGraph() {
		AbstractGraph<Knoten, DefaultEdge> tgraph;
		if(isGerichtet){
			tgraph = new DirectedPseudograph<Knoten, DefaultEdge>(DefaultEdge.class);
			return buildGerichtetAttrUnGewichtet(tgraph);
		}else{
			tgraph = new Pseudograph<Knoten, DefaultEdge>(DefaultEdge.class);
			return buildUnGerichtetAttrUnGewichtet(tgraph);
		}		
	}

	private AbstractGraph<?, DefaultEdge> createUnAttrUnGewichtetGraph() {
		AbstractGraph<String, DefaultEdge> tGraph;
		if(isGerichtet){
			tGraph = new DirectedPseudograph<String, DefaultEdge>(DefaultEdge.class);
			return buildGerichtetUnAttrUnGewichtet(tGraph);
		}else{
			tGraph = new Pseudograph<String, DefaultEdge>(DefaultEdge.class);
			return buildUnGerichtetUnAttrUnGewichtet(tGraph);
		}	
	}

	/////////////////////////////////////////
	// Unterste Ebene
	////////////////////////////////////


	private AbstractGraph<Knoten, DefaultWeightedEdge> buildUnGerichtetAttrGewichtetGraph(AbstractGraph<Knoten, DefaultWeightedEdge> tGraph) {
		CompleteGraphGenerator<Knoten, DefaultWeightedEdge> cGG = new CompleteGraphGenerator<Knoten, DefaultWeightedEdge>(anzahlKanten);		
		return (AbstractGraph<Knoten, DefaultWeightedEdge>) buildKnotenGraph(cGG, tGraph);
	}


	private AbstractGraph<Knoten, DefaultWeightedEdge> buildGerichtetAttrGewichtetGraph(AbstractGraph<Knoten, DefaultWeightedEdge> tGraph) {
		CompleteGraphGenerator<Knoten, DefaultWeightedEdge> cGG = new CompleteGraphGenerator<Knoten, DefaultWeightedEdge>(anzahlKanten);		
		return (AbstractGraph<Knoten, DefaultWeightedEdge>) buildKnotenGraph(cGG, tGraph);
	}
	
	private AbstractGraph<Knoten, DefaultEdge> buildGerichtetAttrUnGewichtet(AbstractGraph<Knoten, DefaultEdge> tgraph) {
		CompleteGraphGenerator<Knoten, DefaultEdge> cGG = new CompleteGraphGenerator<Knoten, DefaultEdge>(anzahlKanten);		
		return (AbstractGraph<Knoten, DefaultEdge>) buildKnotenGraph(cGG, tgraph);
	}


	private AbstractGraph<Knoten, DefaultEdge> buildUnGerichtetAttrUnGewichtet(AbstractGraph<Knoten, DefaultEdge> tgraph) {
		CompleteGraphGenerator<Knoten, DefaultEdge> cGG = new CompleteGraphGenerator<Knoten, DefaultEdge>(anzahlKanten);
		return (AbstractGraph<Knoten, DefaultEdge>) buildKnotenGraph(cGG, tgraph);
	}
	
	private AbstractGraph<String, DefaultEdge> buildUnGerichtetUnAttrUnGewichtet(AbstractGraph<String, DefaultEdge> tGraph) {
		CompleteGraphGenerator<String, DefaultEdge> cGG = new CompleteGraphGenerator<String, DefaultEdge>(anzahlKanten);
		return (AbstractGraph<String, DefaultEdge>) buildStringGraph(cGG, tGraph);
	}

	private AbstractGraph<String, DefaultEdge> buildGerichtetUnAttrUnGewichtet(AbstractGraph<String, DefaultEdge> tGraph) {
		CompleteGraphGenerator<String, DefaultEdge> cGG = new CompleteGraphGenerator<String, DefaultEdge>(anzahlKanten);
		return (AbstractGraph<String, DefaultEdge>) buildStringGraph(cGG, tGraph);
	}

	private AbstractGraph<String, DefaultWeightedEdge> buildUnGerichtetUnAttrGewichtetGraph(AbstractGraph<String, DefaultWeightedEdge> tGraph) {
		CompleteGraphGenerator<String, DefaultWeightedEdge> cGG = new CompleteGraphGenerator<String, DefaultWeightedEdge>(anzahlKanten);
		return (AbstractGraph<String, DefaultWeightedEdge>) buildStringGraph(cGG, tGraph);
	}
	
	private AbstractGraph<String, DefaultWeightedEdge> buildGerichtetUnAttrGewichtetGraph(AbstractGraph<String, DefaultWeightedEdge> tGraph) {
		CompleteGraphGenerator<String, DefaultWeightedEdge> cGG = new CompleteGraphGenerator<String, DefaultWeightedEdge>(anzahlKanten);
		return (AbstractGraph<String, DefaultWeightedEdge>) buildStringGraph(cGG, tGraph);
	}
	
	
	
	private AbstractGraph<String, ?> buildStringGraph(CompleteGraphGenerator generator, AbstractGraph<String, ?> aGraph){
		VertexFactory<String> vertexFactory = new VertexFactory<String>() {
			private int i = 0;
			public String createVertex() {
				return new String(""+ (i++));
			}
		};		
		generator.generateGraph(aGraph, vertexFactory, null);
		Random r = new Random();
		
		int numEdges = aGraph.edgeSet().size();
		System.out.println("numEdges = "+ numEdges);
		for (int i = 0; i < numEdges; i++) {
			Integer from = r.nextInt(anzahlKanten);
			Integer to = r.nextInt(anzahlKanten);
			aGraph.removeEdge(""+from, ""+to);
		}

		return aGraph;
	}
	
	private Set<Integer>  setHeur;
	
	public AbstractGraph<Knoten, ?> buildKnotenGraph(CompleteGraphGenerator generator, AbstractGraph<Knoten, ?> aGraph){
		setHeur = new HashSet<Integer>();
		VertexFactory<Knoten> vertexFactory = new VertexFactory<Knoten>() {
			private int i = 0;
			public Knoten createVertex() {
				if(i == 0){
					int tmp = (i++);
					return new Knoten(""+ tmp,  tmp);	
				}else{
					boolean tmp = true;
					Random r = new Random();
					int n = 0;
					while(tmp){
						n = (r.nextInt(anzahlKanten * 4)+1);
						if(!setHeur.contains(n)){
							setHeur.add(n);
							tmp =false;
						}					
					}
				//	out("Heuristik Set: " + setHeur);
                 //   out("Heuristik Set Size: " +setHeur.size());
					return new Knoten(""+(i++), n);
				}
				
				
			}
		};		
		
		generator.generateGraph(aGraph, vertexFactory, null);
		Random r = new Random();
		
		int numEdges = aGraph.edgeSet().size();
		for (int i = 0; i < 2*numEdges; i++) {  
			Integer from = r.nextInt(anzahlKanten);
			Integer to = r.nextInt(anzahlKanten);
			aGraph.removeEdge(getKnoten(aGraph, ""+from), getKnoten(aGraph, ""+to));
		}
		
		
		Iterator<?> iterEdges = aGraph.edgeSet().iterator();
		while(iterEdges.hasNext()){
			Object obj = iterEdges.next();
			if(obj instanceof DefaultWeightedEdge){
				aGraph = buildHeu(aGraph, (DefaultWeightedEdge) obj);
			}else{
				break;
			}
		}	
		return aGraph;
	}
	
	private Knoten getKnoten(AbstractGraph<Knoten, ?> aGraph, String knotenName){
		Iterator<Knoten> iterVertex =  aGraph.vertexSet().iterator();
		while(iterVertex.hasNext()){
			Knoten a = iterVertex.next();
			if(a.getName().equals(knotenName)) return a;
		}
		return null;
	}
	
	
	private AbstractGraph<Knoten, ?> buildHeu(AbstractGraph<Knoten, ?> aGraph, DefaultWeightedEdge dwe){
		if(aGraph instanceof DefaultDirectedWeightedGraph){
			DefaultDirectedWeightedGraph<Knoten, DefaultWeightedEdge> tmp = (DefaultDirectedWeightedGraph)aGraph; 
			Knoten start = tmp.getEdgeSource(dwe);
			Knoten ende = tmp.getEdgeTarget(dwe);
			tmp.setEdgeWeight(dwe, getKnoten(tmp, start.getName()).dstHeu(getKnoten(aGraph, ende.getName())));			
			return tmp;
		}else if(aGraph instanceof WeightedPseudograph){
			WeightedPseudograph<Knoten, DefaultWeightedEdge> tmp = (WeightedPseudograph)aGraph; 
			Knoten start = tmp.getEdgeSource(dwe);
			Knoten ende = tmp.getEdgeTarget(dwe);
			tmp.setEdgeWeight(dwe, getKnoten(tmp, start.getName()).dstHeu(getKnoten(aGraph, ende.getName())));			
			return tmp;
		}		
		return aGraph;
	}
	
	
	////////////////////////////////////////
	//
	///////////////////////

	void cancelAction(ActionEvent e){
		this.setVisible(false);
	}
	
	private void out(String str){
		System.out.println(str);
	}
	
	private void switchGraph(){
		isAttr = checkAttr.isSelected();
		isGerichtet = checkGerichtet.isSelected();
		isGewichtet = checkGewichtet.isSelected();
	}
	
	private JCheckBox checkGewichtet = new JCheckBox("Gewichtet?");
	private JCheckBox checkAttr = new JCheckBox("Attributiert?");
	private JCheckBox checkGerichtet = new JCheckBox("Gerichtet?");
	
	private JLabel lblAnzahlKnoten = new JLabel("Anzahl an Kanten: ");
	
	private JTextField txtAnzahlKnoten = new JTextField();
	
	private JButton btnCreate = new JButton("Graph erzeugen");
	private JButton btnCancel = new JButton("Cancel");
	
	private boolean isGewichtet =false;
	private boolean isAttr = false;
	private boolean isGerichtet = false;

	
}
